﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;

namespace ReactiveLang.Collections
{
    [ContractClass(typeof(IEnumerableExpressionObserverContract<>))]
    public interface IEnumerableExpressionObserver<in T> : IExpressionObserver<IEnumerable<IExpression<T>>>
    {
        void OnAdded(IExpression<T> item, int index);
        void OnRemoved(IExpression<T> item, int index);
    }

    #region Contract for IEnumerableExpressionObserver<T>

    [ContractClassFor(typeof(IEnumerableExpressionObserver<>))]
    abstract class IEnumerableExpressionObserverContract<T> : IEnumerableExpressionObserver<T>
    {
        public void OnValue(IEnumerable<IExpression<T>> value)
        {
        }

        public void OnException(Exception exception)
        {
        }

        public void OnRemoved(IExpression<T> item, int index)
        {
            Contract.Requires<ArgumentNullException>(item != null, "item");
        }

        public void OnAdded(IExpression<T> item, int index)
        {
            Contract.Requires<ArgumentNullException>(item != null, "item");
        }
    }

    #endregion

    public static partial class EnumerableExpressionObserver
    {
        class AnonymousObserver<T> : IEnumerableExpressionObserver<T>
        {
            internal Action<IEnumerable<IExpression<T>>> _onValue;
            internal Action<IExpression<T>, int> _onAdded, _onRemoved;
            internal Action<Exception> _onException;

            public void OnValue(IEnumerable<IExpression<T>> value)
            {
                _onValue(value);
            }

            public void OnException(Exception error)
            {
                _onException(error);
            }

            public void OnAdded(IExpression<T> item, int index)
            {
                _onAdded(item, index);
            }

            public void OnRemoved(IExpression<T> item, int index)
            {
                _onRemoved(item, index);
            }
        }

        public static IEnumerableExpressionObserver<T> Create<T>(Action<IExpression<T>, int> onAdded, Action<IExpression<T>, int> onRemoved, Action<IEnumerable<IExpression<T>>> onValue, Action<Exception> onException)
        {
            Contract.Requires<ArgumentNullException>(onAdded != null, "onAdded");
            Contract.Requires<ArgumentNullException>(onRemoved != null, "onRemoved");
            Contract.Requires<ArgumentNullException>(onValue != null, "onValue");
            Contract.Requires<ArgumentNullException>(onException != null, "onException");
            return new AnonymousObserver<T>
            {
                _onValue = onValue,
                _onAdded = onAdded,
                _onRemoved = onRemoved,
                _onException = onException
            };
        }
        public static IEnumerableExpressionObserver<T> Create<T>(Action<IExpression<T>, int> onAdded, Action<IExpression<T>, int> onRemoved, Action<IEnumerable<IExpression<T>>> onValue)
        {
            Contract.Requires<ArgumentNullException>(onAdded != null, "onAdded");
            Contract.Requires<ArgumentNullException>(onRemoved != null, "onRemoved");
            Contract.Requires<ArgumentNullException>(onValue != null, "onValue");
            return Create<T>(onAdded, onRemoved, onValue, ex => { throw ex; });
        }

        public static IEnumerableExpressionObserver<T> Empty<T>()
        {
            return Util.GenericStorage<T>.EnumerableObserver;
        }

        internal static void OnCleared<T>(this IEnumerableExpressionObserver<T> observer)
        {
            Contract.Requires<ArgumentNullException>(observer != null, "observer");
            observer.OnValue(Enumerable.Empty<IExpression<T>>());
        }
        internal static void OnAdded<T>(this IEnumerableExpressionObserver<T> observer, IExpression<T> item)
        {
            Contract.Requires<ArgumentNullException>(observer != null, "observer");
            Contract.Requires<ArgumentNullException>(item != null, "item");
            observer.OnAdded(item, -1);
        }
        internal static void OnRemoved<T>(this IEnumerableExpressionObserver<T> observer, IExpression<T> item)
        {
            Contract.Requires<ArgumentNullException>(observer != null, "observer");
            Contract.Requires<ArgumentNullException>(item != null, "item");
            observer.OnRemoved(item, -1);
        }
    }
}
